#ifndef UNITS_H
#define UNITS_H



#include "includes.h"
#include "players.h"



class unit;
class player;



/** @brief Class containing some aditional information needed for visualising the move event
*
*   \image html units.jpeg
*/
class pre_move_info
{

public:

	/** @brief Constructor stores all required information
	*	@param u moving unit, constraint: u != 0
	*	@param x x coordinate of the destination, constraint: x < map->size_x
	*	@param y y coordinate of the destination, constraint: y < map->size_y
	*/
	pre_move_info( unit* u, unsigned int x, unsigned int y );
	//! @brief Deletes path
	~pre_move_info();
	
	//! original x coordinate of the place before the movement
	unsigned int ox;
	//! original y coordinate of the place before the movement
	unsigned int oy;
	//! path of the movement
	queue< pair< unsigned int, unsigned int > >* path;

};



/** @brief Class containing some aditional information needed for visualising the fire event
*
*   \image html units.jpeg
*/
class pre_fire_info
{

public:

	/** @brief Constructor stores all required information
	*	@param u attacking unit, constraint: u != 0
	*	@param x x coordinate of the target, constraint: x < map->size_x
	*	@param y y coordinate of the target, constraint: y < map->size_y
	*/
	pre_fire_info( unit* u, unsigned int x, unsigned int y );

	//! target was also selected unit
	bool s;

};



/** @brief Class containing some aditional information needed for visualising the buy event - not used in this gui
*
*   \image html units.jpeg
*/
class pre_buy_info
{

public:

	/** @brief Constructor stores all required information
	*	@param u unit to buy, constraint: u != 0
	*	@param x x coordinate where to place the unit, constraint: x < map->size_x
	*	@param y y coordinate where to place the unit, constraint: y < map->size_y
	*/
	pre_buy_info( unit* u, unsigned int x, unsigned int y );

};



/** @brief Class containing some aditional information needed for visualising the sell event
*
*   \image html units.jpeg
*/
class pre_sell_info
{

public:

	/** @brief Constructor stores all required information
	*	@param u unit to sell, constraint: u != 0
	*/
	pre_sell_info( unit* u );

	//! x coordinate of the unit
	unsigned int ox;
	//! y coordinate of the unit
	unsigned int oy;

};



/** @brief Base class for all game objects
*
*   \image html units.jpeg
*/
class object
{
	

public:

	/** @brief Constructor sets position and name of the object
	*	@param x x coordinate of the object, constraint x < mapa->size_x
	*	@param y y coordinate of the object, constraint y < mapa->size_y
	*	@param nm name of the object
	*/
	object( unsigned int x, unsigned int y, string nm )
	{	
		pos_x = x;
		pos_y = y;
		name = nm;
	};
	/** @brief	Gets x coordinate of the object
	*	@return x coordinate of the object
	*/
	virtual unsigned int get_x()
	{
		return pos_x;
	};
	/** @brief	Gets y coordinate of the object
	*	@return y coordinate of the object
	*/
	virtual unsigned int get_y()
	{
		return pos_y;
	};
	/** @brief Sets position of the object
	*	@param x x coordinate of the object, constraint x < mapa->size_x
	*	@param y y coordinate of the object, constraint y < mapa->size_y
	*/
	virtual void set_pos( unsigned int x, unsigned int y )
	{
		pos_x = x;
		pos_y = y;
	};
	/** @brief	Gets type of the object
	*	only for dynamic_cast purposes
	*	@return	type of the object
	*/
	virtual int get_type()
	{
		return type;
	};
	/**	@brief	Gets name of the object
	*	@return	name of the object
	*/
	virtual string get_name()
	{
		return name;
	};
	/** @brief	Checks if this object is the same with given
	*	@param	o object to check with
	*	@return true if the objects are the same, else false
	*/
	virtual bool equals( object* o ) = 0;
	/** @brief	Abstract cloning function
	*	@return	clone of the object
	*/
	virtual object* clone() = 0;
	/** @brief Templated function for cloning of children
	*	@return pointer to the clone
	*/
	template< class This >
	This* clonetpl()
	{
		return new This( *static_cast< This* >( this ) );
	};


protected:

	//! x coordinate of the object
	unsigned int pos_x;
	//! y coordinate of the object
	unsigned int pos_y;
	//! object type
	int type;
	//! object's name
	string name;

};



/** @brief Base class for all bonuses
*
*   \image html units.jpeg
*/
class bonus: public object
{

public:

	/**	@brief Constructor sets name and position of the bonus
	*	@param x x coordinate of the bonus, constraint x < mapa->size_x
	*	@param y y coordinate of the bonus, constraint y < mapa->size_y
	*	@param nm name of the bonus
	*/
	bonus( unsigned int x, unsigned int y, string nm ): object( x, y, nm )
	{
		type = 0;
	};	

	/** @brief Init function sets position of the bonus
	*	@param x x coordinate of the bonus, constraint x < mapa->size_x
	*	@param y y coordinate of the bonus, constraint y < mapa->size_y
	*/
	void init( unsigned int x, unsigned int y )
	{
		pos_x = x;
		pos_y = y;
	};

	/** @brief Abstract function called, when unit gets the bonus
	*	@param u unit, that gets the bonus (by moving on it)
	*/
	virtual void effect( unit* u ) = 0;

};



/** @brief Bonus, that adds some extra health to an unit
*
*   \image html units.jpeg
*/
class firstaidkit: public bonus
{

public:

	/**	@brief Constructor sets name and position of the bonus
	*	@param x x coordinate of the bonus, constraint x < mapa->size_x
	*	@param y y coordinate of the bonus, constraint y < mapa->size_y
	*	@param nm name of the bonus
	*/
	firstaidkit( unsigned int x, unsigned int y, string nm ): bonus( x, y, nm ) {};

	/** @brief Gives to an unit 50 health extra and deletes itself
	*	@param u unit, that gets the bonus, constraint: u != 0
	*/
	void effect( unit* u );

	/** @brief	Checks if this object is the same with given
	*	@param	o object to check with
	*	@return true if the objects are the same, else false
	*/
	virtual bool equals( object* o );

	/** @brief	Cloning function
	*	@return pointer to clone of the bonus
	*/
	virtual object* clone()
	{
		return clonetpl< firstaidkit >();
	};

};



/** @brief Class representating an unit in the game
*
*   \image html units.jpeg
*/
class unit: public object
{

public:

	/** @brief Constructor sets all parameters determinating the unit and its owner
	*	@param x x coordinate of the unit, constraint x < mapa->size_x
	*	@param y y coordinate of the unit, constraint y < mapa->size_y
	*	@param p owner of the unit
	*	@param n number of the unit
	*	@param pr price of the unit
	*	@param ra range of the unit
	*	@param he health of the unit
	*	@param sh shield of the unit
	*	@param we power of the weapon
	*	@param cm time needed to move over one square
	*	@param cf time needed to fire
	*	@param nm name of the unit
	*/
	unit( unsigned int x, unsigned int y, player* p, int n, unsigned int pr, unsigned int ra, unsigned int he, unsigned int sh, unsigned int we, unsigned int cm, unsigned int cf, string nm ): object ( x, y, nm )
	{
		pl = p;
		nbr = n;
		type = 1;
		price = pr;
		range = ra;
		health = he;
		shield = sh;
		weapon = we;
		cost_move = cm;
		cost_fire = cf;	
	};

	/** @brief Init function sets units position, owner and name
	*	@param x x coordinate of the unit, constraint x < mapa->size_x
	*	@param y y coordinate of the unit, constraint y < mapa->size_y
	*	@param p owner of the unit
	*	@param n number of the unit
	*/
	void init( unsigned int x, unsigned int y, player* p, int n )
	{
		pos_x = x;
		pos_y = y;
		pl = p;
		nbr = n;
	};

	/** @brief	Function for moving
	*	@param	x destination x coordinate, constraint x < mapa->size_x
	*	@param	y destination y coordinate, constraint y < mapa->size_y
	*	@return	true, if movement is sucessful, else false
	*/
	bool move( unsigned int x, unsigned int y );
	/** @brief	Function for attacking
	*	@param	x x coordinate of the target, constraint x < mapa->size_x
	*	@param	y y coordinate of the target, constraint y < mapa->size_y
	*	@return	true, if attack is successful, else false
	*/
	bool fire( unsigned int x, unsigned int y );
	/** @brief	Gets, how much does it cost to move over one square
	*	@return	cost_move parameter
	*/
	unsigned int get_cost_move()
	{
		return cost_move;
	};
	/**	@brief	Gets, how much does it cost to fire
	*	@return	cost_fire parameter
	*/
	unsigned int get_cost_fire()
	{
		return cost_fire;
	};
	/** @brief	Gets price of the unit
	*	@return	price parameter
	*/
	unsigned int get_price()
	{
		return price;
	};
	/**	@brief	Gets range of the unit
	*	@return	range parameter
	*/
	unsigned int get_range()
	{
		return range;
	};
	/**	@brief	Gets health of the unit
	*	@return	current health of the unit
	*/
	int get_health()
	{
		return health;
	};
	/**	@brief	Gets unit's shield
	*	@return shield parameter
	*/
	unsigned int get_shield()
	{
		return shield;
	};
	/**	@brief	Gets power of unit's weapon
	*	@return	weapon parameter
	*/
	unsigned int get_weapon()
	{
		return weapon;
	};
	/**	@brief Sets unit's health
	*	@param h health to be set
	*/
	void set_health( unsigned int h )
	{
		health = h;
	};
	/**	@brief	Gets some info about the unit
	*	@return	unit's price, range, health, shield, weapon, cost_move and cost_fire
	*/
	string get_info();
	/** @brief Function called, when the unit is hit by an opponent
	*	@param w power of opponent's weapon
	*/
	void hit( unsigned int w );
	/** @brief	Gets owner of the unit
	*	@return	owner of the unit
	*/
	player* get_player()
	{
		return pl;
	};
	/** @brief Sets owner of the unit
	*	@param p owner of the unit
	*/
	void set_player( player* p )
	{
		pl = p;
	};
	/** @brief	Gets type of the unit
	*	@return	type of the unit
	*/
	int get_type()
	{
		return type;
	};
	/**	@brief	Gets nmber of the unit
	*	@return number of the unit
	*/
	int get_nbr()
	{
		return nbr;
	};
	/** @brief	Computes an unique id within a player
	*	@return	an unique identification number
	*/
	int get_id();

	/** @brief	Checks if this object is the same with given
	*	@param	o object to check with
	*	@return true if the objects are the same, else false
	*/
	virtual bool equals( object* o );

	/** @brief	Cloning function
	*	@return pointer to clone of the unit
	*/
	virtual object* clone()
	{
		return clonetpl< unit >();
	};


protected:

	/** @brief	Function for handling a move event
	*	@param	path vector to be filled with coordinates of every movement of the unit, constraint: path != 0
	*	@param	x destination x coordinate, constraint x < mapa->size_x
	*	@param	y destination y coordinate, constraint y < mapa->size_y
	*	@return	true, if movement is sucessful, else false
	*/
	bool move_handle( queue< pair< unsigned int, unsigned int > >* path, unsigned int x, unsigned int y );
	/** @brief	Function for handling a fire event
	*	@param	x x coordinate of the target, constraint x < mapa->size_x
	*	@param	y y coordinate of the target, constraint y < mapa->size_y
	*	@return	true, if attack is successful, else false
	*/
	bool fire_handle( unsigned int x, unsigned int y );

	//! owner of the unit
	player* pl;
	//! unit's price
	unsigned int price;
	//! unit's range
	unsigned int range;
	//! current unit's health
	int health;
	//! unit's shield
	unsigned int shield;
	//! power of unit's weapon
	unsigned int weapon;
	//! time needed to move over one square
	unsigned int cost_move;
	//! time needed to fire
	unsigned int cost_fire;	
	//! number of the unit
	int nbr;

};



/** @brief Class storing prototypes of units and bonuses
*
*   \image html units.jpeg
*/
class prototypes
{

public:

	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static prototypes* get_instance();

	//! @brief Nothing to be done there
	void rebuild();

	/**	@brief Sets new unit to prototypes map
	*	@param nm name of the unit
	*	@param pr price of the unit
	*	@param ra range of the unit
	*	@param he health of the unit
	*	@param sh shield of the unit
	*	@param we power of the weapon
	*	@param cm time needed to move over one square
	*	@param cf time needed to fire
	*/
	void set_new_unit( const string& nm, unsigned int pr, unsigned int ra, unsigned int he, unsigned int sh, unsigned int we, unsigned int cm, unsigned int cf )
	{
		units[ nm ] = new unit( 0, 0, 0, 0, pr, ra, he, sh, we, cm, cf, nm );
	};

	/** @brief	Loads all types of units from file
	*	@param	file filename, where the arameters are stored
	*	file structure: "Name Price Range Health Shield Weapon Cost_move Cost_fire Picture_normal_1 Picture_selected_1 .. Picture_normal_MAX_PL Picture_selected_MAX_PL Sound_fire"
	*	#: comment
	*	@return	true, if loading is successful, else false
	*/
	bool load_types( const char* file );

	/** @brief	Converts units name to position in prototypes map
	*	@param	nm units name
	*	@return	position of the unit if found, else -1
	*/
	int units_name_pos( const string& nm );

	/** @brief	Converts units prototypes map iterator to position
	*	@param	it units prototypes map iterator
	*	@return	position of the unit if found, else -1
	*/
	int units_iterator_pos( map< string, unit* >::iterator it );

	/**	@brief	Converts units prototype position to iterator
	*	@param	p units prototype position
	*	@return iterator to the unit prototype, or units.end() if not found
	*/
	map< string, unit* >::iterator unit_pos_iterator( unsigned int p );

	//! map of units prototypes
	map< string, unit* > units;
	//! map of bonuses prototypes
	map< string, bonus* > bonuses;


	int x;


private:

	/**	@brief Constructor fills bonuses prototypes map
	*	@param _x singleton stuff
	*/
	prototypes( int _x ): x( _x )
	{
		bonuses[ "First aid kit" ] = new firstaidkit( 0, 0, "First aid kit" );
	};
	prototypes( const prototypes& );
	prototypes& operator=( const prototypes& x );
	//! @brief Deletes all prototypes from maps
	~prototypes()
	{
		for ( map< string, unit* >::iterator it = units.begin(); it != units.end(); ++it )
			delete it->second;
		for ( map< string, bonus* >::iterator it = bonuses.begin(); it != bonuses.end(); ++it )
			delete it->second;
	};


	/** @brief	Parses lines when loading unit types
	*	@param	ul line to parse
	*	@return	true if parsing successful, else false
	*/
	bool parse_unit_line( const string& ul );

};



#endif
